package org.swift.mashup.engine.model.processors;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNode;
import org.swift.mashup.engine.model.ActivityContext;
import org.swift.mashup.engine.model.ByteArrayActivityContext;

public class OctaveToXmlProcessor implements FromProcessor {

	protected static final String TAG_ROOT = "value";
	protected static final String TAG_ROW = "row";
	protected static final String TAG_COLUMN = "col";
	protected static final String ATTR_INDEX = "idx";

	private static final String META_TYPE = "type";
	protected static final String META_TYPE_STRING = "string";
	protected static final String META_TYPE_SCALAR = "scalar";

	protected static final String META_DELIMITER = "# ";
	protected static final String ROW_DELIMITER = "[\\n;]";
	protected static final String COLUMN_DELIMITER = "[\\s,]";
	
	protected static final Pattern META_LINE_PATTERN = Pattern.compile(META_DELIMITER+"([A-z]+): ([A-z_0-9]+)");
	protected static final int META_LINE_PATTERN_GROUPNUM_KEY = 1;
	protected static final int META_LINE_PATTERN_GROUPNUM_VALUE = 2;

	private static OctaveToXmlProcessor instance = new OctaveToXmlProcessor();
	
	/**
	 * Takes octave text data and transforms it into simple xml. 
	 */
	public ActivityContext processData(ActivityContext data) {
		try {
			String dataAsString = new String(data.getBytes());
			List<String> lines = getLines(dataAsString);
			
			Map<String,String> meta = processMeta(lines);
			String value = processValue(lines);
			
			OMElement xml = createXml(meta, value);
			ByteArrayOutputStream output = new ByteArrayOutputStream();
			xml.serialize(output);
			return new ByteArrayActivityContext(output.toByteArray());
		}
		catch (Exception e) {
			throw new RuntimeException("Error processing data",e);
		}
	}
	
	public static OctaveToXmlProcessor getInstance(){
		return instance;
	}

	protected List<String> getLines(String text) {
		List<String> lines = new ArrayList<String>();
		String[] linearr = text.split("\\n");

		for ( String line : linearr ) {
			if ( line.trim().length() > 0 ) {
				lines.add(line);
			}
		}
		
		return lines;
	}
	
	protected Map<String,String> processMeta(List<String> lines) {
		Map<String,String> metamap = new HashMap<String,String>();
		
		for ( String line : lines ) {
			if ( isMetaLine(line) ) {
				processMetaLine(line, metamap);
			}
		}
		
		return metamap;
	}
	
	protected boolean isMetaLine(String line) {
		return line.startsWith(META_DELIMITER);
	}

	protected void processMetaLine(String line, Map<String,String> metamap) {
		Matcher matcher = META_LINE_PATTERN.matcher(line);
		
		if ( matcher.matches() ) {
			String key = matcher.group(META_LINE_PATTERN_GROUPNUM_KEY);
			String val = matcher.group(META_LINE_PATTERN_GROUPNUM_VALUE);
			
			metamap.put(key, val);
		}
	}

	protected String processValue(List<String> lines) {
		StringBuilder sb = new StringBuilder();
		
		boolean isFirst = true;
		for ( String line : lines ) {
			if ( !isMetaLine(line) ) {
				if ( isFirst ) {
					isFirst = false;
				}
				else {
					sb.append('\n');
				}
				sb.append(line);
			}
		}
		
		return sb.toString();
	}

	protected OMElement createXml(Map<String,String> metamap, String value) {
		OMFactory factory = OMAbstractFactory.getOMFactory();
		OMElement rootNode = factory.createOMElement(TAG_ROOT, null);
		
		List<OMAttribute> attrs = createMetaAttrs(factory, metamap);
		List<OMNode> valueNodes = createValueNodes(factory, value, metamap.get(META_TYPE));
		
		for ( OMAttribute attr : attrs ) {
			rootNode.addAttribute(attr);
		}
		
		for ( OMNode node : valueNodes ) {
			rootNode.addChild(node);
		}

		return rootNode;
	}

	protected List<OMAttribute> createMetaAttrs(OMFactory factory, Map<String, String> metamap) {
		List<OMAttribute> attrs = new ArrayList<OMAttribute>();
		
		for ( Map.Entry<String,String> entry : metamap.entrySet() ) {
			attrs.add( factory.createOMAttribute(entry.getKey(), null, entry.getValue()) );
		}
		
		return attrs;
	}

	protected List<OMNode> createValueNodes(OMFactory factory, String value, String type) {
		if ( type.equals(META_TYPE_STRING) || type.equals(META_TYPE_SCALAR) ) {
			return createStringNode(factory, value);
		}
		else {
			return createRowNodes(factory, value);
		}
	}

	protected List<OMNode> createStringNode(OMFactory factory, String value) {
		return Arrays.asList((OMNode)factory.createOMText(value));
	}

	protected List<OMNode> createRowNodes(OMFactory factory, String value) {
		List<OMNode> rowNodes = new ArrayList<OMNode>();
		
		String[] rows = value.split(ROW_DELIMITER);
		int num = 1;
		for ( String row : rows ) {
			OMElement rowNode = factory.createOMElement(TAG_ROW, null);
			rowNode.addAttribute( factory.createOMAttribute(ATTR_INDEX, null, String.valueOf(num++)) );
			
			List<OMNode> colNodes = createColumnNodes(factory, row);
			for ( OMNode colNode : colNodes ) {
				rowNode.addChild(colNode);
			}
			
			rowNodes.add(rowNode);
		}
		
		return rowNodes;
	}

	protected List<OMNode> createColumnNodes(OMFactory factory, String row) {
		List<OMNode> colNodes = new ArrayList<OMNode>();
		
		String[] cols = row.split(COLUMN_DELIMITER);
		int num = 1;
		for ( String col : cols ) {
			if ( col.trim().length() > 0 ) {
				OMElement colNode = factory.createOMElement(TAG_COLUMN, null);
				colNode.addAttribute( factory.createOMAttribute(ATTR_INDEX, null, String.valueOf(num++)) );
				colNode.addChild( factory.createOMText(col) );
				colNodes.add(colNode);
			}
		}
		
		return colNodes;
	}
}
